package com.njxxted.activiti.modules.process.model.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Maps;
import com.njxxted.activiti.common.constant.Constant;
import com.njxxted.activiti.common.exception.CustomerException;
import com.njxxted.activiti.common.response.ResponseVo;
import com.njxxted.activiti.common.util.BpmnBuilderUtil;
import com.njxxted.activiti.common.util.FeignUtil;
import com.njxxted.activiti.common.util.StringUtils;
import com.njxxted.activiti.common.util.WorkflowUtils;
import com.njxxted.activiti.modules.api.CommonApi;
import com.njxxted.activiti.modules.base.service.BaseService;
import com.njxxted.activiti.modules.process.cmd.BpmnModelCmd;
import com.njxxted.activiti.modules.process.model.service.IActModelService;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 描述：流程引擎模型 service 实现类
 * <p>
 * 作者：Ostrich Hu
 * 时间：2019/8/30 16:59 星期五
 */
@Service
public class IActModelServiceImpl extends BaseService implements IActModelService {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    ObjectMapper objectMapper;

    @Override
    public Model createBpmnModel(BpmnModelCmd bpmnModelCmd) {
        //初始化一个空模型
        Model model = repositoryService.newModel();
        model.setKey(bpmnModelCmd.getKey());
        model.setName(bpmnModelCmd.getName());
        model.setCategory(bpmnModelCmd.getCategory());
        model.setVersion(1);

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, model.getName());
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, model.getVersion());
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, bpmnModelCmd.getDescription());
        //设置元信息
        model.setMetaInfo(modelNode.toString());
        repositoryService.saveModel(model);

        //完善ModelEditorSource
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", Constant.Flow.PROCESS_NAME_SPACE);
        editorNode.set("stencilset", stencilSetNode);
        repositoryService.addModelEditorSource(model.getId(), editorNode.toString().getBytes(StandardCharsets.UTF_8));

        logger.info("创建模型名称：{}  创建模型Id：{}", model.getName(), model.getId());
        return model;
    }

    @Override
    public ResponseVo getModel(HttpServletRequest request) {
        BpmnModelCmd bpmnModelCmd = BpmnBuilderUtil.buildBpmnCmd(request);
        ModelQuery modelQuery = repositoryService.createModelQuery();
        if (bpmnModelCmd.getCategory() != null) {
            modelQuery.modelCategory(bpmnModelCmd.getCategory());
        }
        if (bpmnModelCmd.getKey() != null) {
            modelQuery.modelKey(bpmnModelCmd.getKey());
        }
        if (bpmnModelCmd.getName() != null) {
            modelQuery.modelNameLike(bpmnModelCmd.getName());
        }
        List<Model> models = modelQuery.listPage(bpmnModelCmd.getPageNum() - 1, bpmnModelCmd.getPageSize());
        long count = modelQuery.count();
        HashMap<Object, Object> map = Maps.newHashMap();
        map.put("result", models);
        map.put("total", count);
        map.put("pageNum", bpmnModelCmd.getPageNum());
        map.put("pageSize", bpmnModelCmd.getPageSize());
        return ResponseVo.ok(map);
    }

    @Override
    public boolean deleteModel(String modelId, String isEmpty) {
        try {
            Model model = repositoryService.getModel(modelId);
            //if model's name and model's key is null and then delete it
            if (model != null && "N".equals(isEmpty)) {
                repositoryService.deleteModel(modelId);
            }else if(model != null && "Y".equals(isEmpty) && model.getName() == null && model.getKey() == null) {
                repositoryService.deleteModel(modelId);
            }
            return true;
        } catch (Exception e) {
            throw new CustomerException("模型" + modelId + "删除失败", e);
        }

    }

    @Override
    public boolean deploymentModel(String modelId) {
        try {
            Model model = repositoryService.getModel(modelId);
            String modelName = model.getName();
            String modelCategory = model.getCategory();
            String defResourceName = model.getKey() + ".bpmn20.xml";
            String imgResourceName = model.getKey() + "." + model.getKey() + ".png";
            byte[] modelEditorSource = repositoryService.getModelEditorSource(modelId);
            //读取editorSource
            JsonNode jsonNode = objectMapper.readTree(modelEditorSource);
            //转换editorSource为bpmnModel
            BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode);
            byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(bpmnModel);
            //根据 BpmnModel 生成图片输入流
            InputStream flowImage = WorkflowUtils.getFlowImage(bpmnModel);

            Deployment deployment = repositoryService.createDeployment().name(modelName).category(modelCategory)
                    .addString(defResourceName, new String(bpmnBytes))
                    .addInputStream(imgResourceName, flowImage)
//                    .enableDuplicateFiltering()
                    .deploy();

            logger.info("模型：{}\n部署名称：{}\n部署Id：{}", modelName, deployment.getName(), deployment.getId());
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .deploymentId(deployment.getId()).singleResult();

            if (processDefinition == null) {
                return false;
            }
            //部署之后更新模型表中部署id字段
            model.setDeploymentId(deployment.getId());
            repositoryService.saveModel(model);
            repositoryService.setDeploymentCategory(deployment.getId(), modelCategory);
            repositoryService.setDeploymentKey(deployment.getId(), model.getKey());
            repositoryService.setProcessDefinitionCategory(processDefinition.getId(), modelCategory);
            return true;
        } catch (Exception e) {
            throw new CustomerException("模型id[" + modelId + "]部署失败", e);
        }
    }

    @Override
    public ResponseVo getFlowNode(String key) {
        logger.debug("流程定义key：{}", key);
        if (StringUtils.isEmpty(key)) {
            return ResponseVo.error();
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(key).latestVersion().singleResult();
        if (processDefinition == null) {
            return ResponseVo.error("请先启动流程");
        }
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
        List<Map<String, Object>> allNodeInProcess = WorkflowUtils.getAllNodeInProcess(bpmnModel);
        return ResponseVo.ok(allNodeInProcess);
    }

    @Override
    public ResponseVo getModelForStartInstance(String type) {
        if (StringUtils.isEmpty(type)) {
            return ResponseVo.error("字典类型参数为空");
        }
        ResponseVo responseVo = new FeignUtil<CommonApi>().invokeCommonService(CommonApi.class).getKVList(type);
        List<Map> mapList = JSONObject.parseArray(JSON.toJSONString(responseVo.get("data")), Map.class);
        mapList.forEach(map -> {
            String code = map.get("code").toString();
            List<Model> models = repositoryService.createModelQuery().modelCategory(code).list();
            map.put("child", models);
        });
        mapList.removeIf(map -> Objects.equals(map.get("pId"), "0"));
        return ResponseVo.ok(mapList);
    }
}
